#pragma once
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Logger.hpp"
#include "Thread.hpp"
#include <vector>
#include <string>
#include <queue>
#include <memory>

static const int default_size = 3;
static const int default_cap = 5;
template <class T>
class ThreadPool
{
private:
    void Run()
    {
        // 线程池初始化完后，实际的内核级的线程并没有被真正创建和运行
        for (auto &e : _pool)
        {
            e.Start();
            LOG(LogLevel::INFO) << e.GetName() << ":开始运行";
        }
    }

    void Stop()
    {
        // 线程池终止:所有线程退出+join相关线程
        // 线程池设置为退出状态：在此状态下，其余线程的情况：在条件变量中等待的；不在条件变量中等待--->在条件变量等待之前的(完成条件判断，未完成条件判断)，在等待之后的(在执行任务)
        // 核心思路：让所有线程都进入条件变量的等待状态(或者根本无法进入)--->统一唤醒，执行相应的终止逻辑
        if (!_running)
            return;
        {
            LockGuard lg(&_mutex);
            _running = false;
            if (_c_wait > 0)
                _cond_c.NotifyAll();
        }
    }

    void Join()
    {
        for (auto &e : _pool)
        {
            e.Join();
            // LOG(LogLevel::INFO) << e.GetName() << ":join 成功";
        }
    }

    ThreadPool(int size = default_size, int cap = default_cap) : _sz(size), _cap(cap), _c_wait(0), _p_wait(0), _running(true)
    {
        for (int i = 0; i < _sz; i++)
        {
            std::string name = "thread-" + std::to_string(i);
            _pool.emplace_back([this](const std::string &name)
                               { this->Routine(name); }, name);

            LOG(LogLevel::INFO)
                << name << ":创建成功";
        }
        Run(); // RAII
    }

    ThreadPool(const ThreadPool<T>&) = delete;
    ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;

public:
    void Routine(const std::string &name)
    {
        // 访问临界资源通知队列
        while (true)
        {
            T task;
            {
                LockGuard lg(&_mutex);

                while (_q.empty() && _running) // 线程池为运行状态
                {
                    _c_wait++;
                    _cond_c.Wait(_mutex);
                    _c_wait--;
                }
                // 三种情况：正常情况；非正常情况：队列空或非空
                if (_q.empty())
                {
                    LOG(LogLevel::INFO) << name << ":线程退出";
                    break;
                }
                // 满足条件，访问临界资源:正常或非但队列非空
                task = _q.front();
                _q.pop();
                if (_p_wait > 0)
                    _cond_p.NotifyOne(); // 避免惊群问题v
            }
            LOG(LogLevel::INFO) << name << ":执行任务";
            task(); // 本质就是一个函数
        }
    }

    void Enqueue(T task)
    {
        {
            LockGuard lg(&_mutex);
            while (_q.size() == _cap)
            {
                _p_wait++;
                _cond_p.Wait(_mutex);
                _p_wait--;
            }

            // 满足条件，入队列
            _q.push(task);
            if (_c_wait > 0)
                _cond_c.NotifyOne();
        }
    }

    static std::unique_ptr<ThreadPool<T>> &GetInstance()
    {
        {
            LockGuard lg(&_i_mutex);
            if (!_instance)
            {
                LOG(LogLevel::INFO) << "实例不存在,创建实例";
                _instance = std::unique_ptr<ThreadPool<T>>(new ThreadPool<T>());//这样才可以使用私有构造
            }
        }

        LOG(LogLevel::INFO) << "实例已经存在";
        return _instance;
    }

    ~ThreadPool()
    {
        Stop();
        Join();
    }

private:
    std::vector<Thread> _pool;
    bool _running;
    int _c_wait;      // 消费者在条件变量下的等待数量
    int _p_wait;      // 生产者在条件变量下的等待数量
    int _sz;          // 线程池线程个数
    std::queue<T> _q; // 通知队列设计成模板
    int _cap;         // 通知队列最大元素个数

    // 通过static类中指针设计成单例模式

    static std::unique_ptr<ThreadPool<T>> _instance; // 单实例
    static Mutex _i_mutex;                           // 用于保护获取单实例的锁

    Mutex _mutex; // 共用同一把锁
    Cond _cond_c; // 消费者对应的条件变量
    Cond _cond_p; // 生产者对应的条件变量
};

template <class T>
std::unique_ptr<ThreadPool<T>> ThreadPool<T>::_instance; // 默认为nullptr
template <class T>
Mutex ThreadPool<T>::_i_mutex;